package nl.hanze.t23i.gamemodule.game;

import java.awt.Component;
import java.util.HashMap;

import nl.hanze.t23i.gamemodule.extern.AbstractGameModule;

public class Othello extends AbstractGameModule {
	
    public static final String GAME_TYPE = Game.GAME_TYPE;

    //public static final int EMPTY = 2;
	
    public static final int HUMAN_WIN    = 0;
    public static final int DRAW         = 1;
    public static final int UNCLEAR      = 2;
    public static final int COMPUTER_WIN = 3;
	

    private static final int DIMENSION = 8;
    public static final int EMPTY = 0;
    private static final int PLAYERONE = 1;
    private static final int PLAYERTWO = 2;

    private String moveDetails;
    private String nextPlayer;
    private HashMap<String, Integer> playersSide;
    private HashMap<String, Integer> playerResults;
    private HashMap<String, Integer> playerScores;

    public int[][] board = new int[DIMENSION][DIMENSION];
	private static final int N = 0;
	private static final int NE= 1;
	private static final int E = 2;
	private static final int SE= 3;
	private static final int S = 4;
	private static final int SW= 5;
	private static final int W = 6;
	private static final int NW= 7;
	public int[] flips = new int[NW+1];
	
	public Othello(String playerOne, String playerTwo) {
		super(playerOne, playerTwo);
		
		playerResults = new HashMap<String, Integer>();
		playerScores = new HashMap<String, Integer>();
		
		playersSide = new HashMap<String, Integer>();
		playersSide.put(playerOne, PLAYERONE);
		playersSide.put(playerTwo, PLAYERTWO);

		clearBoard();
		calculateScoresAndResults();
	}
	
	@Override
	public void doPlayerMove(String player, String move) throws IllegalStateException {
		super.doPlayerMove(player, move);

		if(!nextPlayer.equals(player))
			throw new IllegalStateException("Het is jouw beurt niet!");

		try {
			//1. Kijk of deze move kan
			if(moveOk(Integer.parseInt(move))){	
				//2. Zo ja, doe deze move (rekent ook de gevolgen voor het spelbord uit)
				playMove(Integer.parseInt(move));
			} else {
				illegalPlayerMove(player);
				return;
			}
		} catch (NumberFormatException e) {
			illegalPlayerMove(player);
			return;
		}
		
		//3. Reken de scores opnieuw uit en het resultaat als dit de laatste zet was.
		calculateScoresAndResults();
		
		//4. Als er geen zetten meer mogelijk zijn, handel dit af
		if (gameEnded()) {
			matchStatus = MATCH_FINISHED;
			String score = getPlayerScore(playerOne) + "-" + getPlayerScore(playerTwo);
			String winZin = " heeft gewonnen met " + score;
			if (getPlayerResult(playerOne)==PLAYER_WIN)
				moveDetails = playerOne + winZin;
			if (getPlayerResult(playerTwo)==PLAYER_WIN)
				moveDetails = playerTwo + winZin;
			else
				moveDetails = "Het spel eindigt in gelijk spel (" + score + ")";
		} else {
			//5. Zo niet, ga dan door met het spel
			moveDetails = player + " heeft een zet gedaan.";
			nextPlayer();
		}
	}
	
	@Override
	public String getMatchResultComment() throws IllegalStateException {
		super.getMatchResultComment();
		
		return "Einde spel!";
	}

	@Override
	public int getMatchStatus() {
		return matchStatus;
	}

	@Override
	public String getMoveDetails() throws IllegalStateException {
		super.getMoveDetails();
		if(moveDetails == null) {
            throw new IllegalStateException("Er is geen zet gedaan tijdens deze beurt!");
		}
		return moveDetails;
	}

	@Override
	public String getPlayerToMove() throws IllegalStateException {
		super.getPlayerToMove();
		
		return nextPlayer;
	}

	@Override
	public int getPlayerResult(String player) throws IllegalStateException {
		super.getPlayerResult(player);
		
		return playerResults.get(player);
	}
	
	@Override
	public int getPlayerScore(String player) throws IllegalStateException {
		super.getPlayerScore(player);
		
		return playerScores.get(player);
	}

	@Override
	public String getTurnMessage() throws IllegalStateException {
		super.getTurnMessage();
		
		String message = null;
		
		if(moveDetails == null) {
			message = "Plaats uw zet; getal tussen de 0 en de "+(DIMENSION*DIMENSION-1);
		} else {
			message = moveDetails;
		}
		
		return message;
	}

	@Override
	public Component getView() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void start() throws IllegalStateException {
		super.start();
		nextPlayer = playerOne;
	}

	private void nextPlayer() {
		nextPlayer = otherPlayer(nextPlayer);
	}
	
	private String otherPlayer(String player) {
		return player.equals(playerOne) ? playerTwo : playerOne;
	}
	
	private void illegalPlayerMove(String player) {
		moveDetails = "Illegal move";
	}

	public boolean moveOk(int move) {
		return moveOk(move, nextPlayer);
	}
	public boolean moveOk(int move, String player) {
		if (move<0 || move > DIMENSION*DIMENSION-1)
			return false;

		int x = xFromMove(move);
		int y = yFromMove(move);
		if (board[y][x]!=EMPTY)
			return false;

		clearFlips();
		boolean foundOnePotential = false;
		for (int i=1; i<DIMENSION; i++) {
			if (i==2) {
				for (int dir = N; dir <= NW; dir++) {
					if (flips[dir]>0) {
						foundOnePotential = true;
						break;
					}
				}
				if (!foundOnePotential)
					return false;
			}
			doFlip(i, y-i, x  , playersSide.get(otherPlayer(player)), N );
			doFlip(i, y-i, x+i, playersSide.get(otherPlayer(player)), NE);
			doFlip(i, y  , x+i, playersSide.get(otherPlayer(player)), E );
			doFlip(i, y+i, x+i, playersSide.get(otherPlayer(player)), SE);
			doFlip(i, y+i, x  , playersSide.get(otherPlayer(player)), S );
			doFlip(i, y+i, x-i, playersSide.get(otherPlayer(player)), SW);
			doFlip(i, y  , x-i, playersSide.get(otherPlayer(player)), W );
			doFlip(i, y-i, x-i, playersSide.get(otherPlayer(player)), NW);
		}
		for (int dir = N; dir <= NW; dir++) {
			if (flips[dir]>0)
				return true;
		}
		return false;
	}
	private void doFlip(int i, int y, int x, int other, int dir) {
		if (x<0 || x>DIMENSION-1 ||
			y<0 || y>DIMENSION-1) {
			if (flips[dir] == i-1)
				flips[dir]=0;
		} else if (board[y][x]==other && flips[dir]==i-1) {
			flips[dir]++;
		} else if (board[y][x]==EMPTY && flips[dir]==i-1) {
			flips[dir]=0;
		}
	}
	
	public boolean gameEnded() {
		for (int n = 0; n < DIMENSION*DIMENSION-1; n++) {
			if (moveOk(n, playerOne) || moveOk(n, playerTwo)) {
				return false;
			}
		}
		return true;
	}
	
	public void playMove(int move) {
		int x = xFromMove(move);
		int y = yFromMove(move);
		int player = playersSide.get(nextPlayer);
		
		board[y][x] = player;
		
		int dx = 0, dy = 0;
		for (int dir = N; dir <= NW; dir++) {
			switch (dir) {
				case N:	dx= 0; dy=-1; break;
				case NE:dx=+1; dy=-1; break;
				case E:	dx=+1; dy= 0; break;
				case SE:dx=+1; dy=+1; break;
				case S:	dx= 0; dy=+1; break;
				case SW:dx=-1; dy=+1; break;
				case W:	dx=-1; dy= 0; break;
				case NW:dx=-1; dy=-1; break;
			}
			for (int i = 1; i <= flips[dir]; i++) {
				board[y+dy*i][x+dx*i]=player;
			}
		}
	}
	private int xFromMove(int move) {
		return move%DIMENSION;
	}
	private int yFromMove(int move) {
		return move/DIMENSION;
	}
	private void clearFlips() {
		for (int dir = N; dir <= NW; dir++) {
			flips[dir]=0;
		}
	}
	
	protected void calculateScoresAndResults() {
		playerScores.put(playerOne, 0);
		playerScores.put(playerTwo, 0);
		for (int i = 0; i < DIMENSION*DIMENSION-1; i++) {
			if (board[yFromMove(i)][xFromMove(i)]==playersSide.get(playerOne))
				playerScores.put(playerOne, playerScores.get(playerOne)+1);
			if (board[yFromMove(i)][xFromMove(i)]==playersSide.get(playerTwo))
				playerScores.put(playerTwo, playerScores.get(playerTwo)+1);
		}
		if (playerScores.get(playerOne) > playerScores.get(playerTwo)) {
			playerResults.put(playerOne, PLAYER_WIN);
			playerResults.put(playerTwo, PLAYER_LOSS);
		} else if (playerScores.get(playerOne) < playerScores.get(playerTwo)) {
			playerResults.put(playerOne, PLAYER_LOSS);
			playerResults.put(playerTwo, PLAYER_WIN);
		} else {
			playerResults.put(playerOne, PLAYER_DRAW);
			playerResults.put(playerTwo, PLAYER_DRAW);
		}
	}
	
	protected void clearBoard() {
		for (int y = 0; y < DIMENSION; y++) {
			for (int x = 0 ; x < DIMENSION; x++) {
				if (x > 2 && x < 5 && y == 3) {
					board[y][x]=playersSide.get(playerOne);
					continue;
				}
				if (x > 2 && x < 5 && y == 4) {
					board[y][x]=playersSide.get(playerTwo);
					continue;
				}
				board[y][x] = EMPTY;
			}
		}
	}
}
